﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EzDesk.Apps.Model.Attendance;

namespace EzDesk.Apps.ServiceImpl.Attendance
{
    public class AttendanceEvaluator
    {
        private List<AttendanceInfo> _attendanceInfoList;
        private DayInfo _dayInfo;
        private DayLeaveInfo _dayLeaveInfo;
        private AttendanceConfig _attendanceConfig;
        private int _extraManhours;
        private Guid _userId;



        // 当日正常考勤获得的工时
        private int? _normalAttendanceManhours;
        // 当日非正常考勤获得的工时
        private int? _abNormalAttendanceManhours;
        private int _lateMiniutes = 0;
        private int _earlyMiniutes = 0;

        public AttendanceEvaluator(
            Guid userId,
            AttendanceConfig config,
            List<AttendanceInfo> attendanceInfoList,
            Model.Attendance.DayInfo dayInfo,
            Model.Attendance.DayLeaveInfo dayLeaveInfo,
            int extraManhours)
        {
            _normalAttendanceManhours = null;
            _abNormalAttendanceManhours = null;

            this._attendanceConfig = config;
            this._userId = userId;
            this._attendanceInfoList = attendanceInfoList;
            this._dayInfo = dayInfo;
            this._dayLeaveInfo = dayLeaveInfo;
            this._extraManhours = extraManhours;

            this.init();
        }

        private void init()
        {
            // 找出正常考勤相关情况
            List<AttendanceInfo> normalAttendanceInfo;
            bool isIncludeNormalAttendanceInfo = tryGetNormalAttendanceInfo(_attendanceInfoList, out normalAttendanceInfo);

            if (isIncludeNormalAttendanceInfo)
            {
                // 计算当日标准的上下班时间
                initLateAndEarlyMiniutes(normalAttendanceInfo);
                initNormalAttendanceManHours(_lateMiniutes, _earlyMiniutes);
            }

            // 计算所有非正常考勤相关的工时
            IEnumerable<AttendanceInfo> abnormalAttendanceInfo = getAbnormalAttendanceInfo(_attendanceInfoList);
            initAbnormalAttendanceManHours(abnormalAttendanceInfo, _dayLeaveInfo);
        }

        private void initLateAndEarlyMiniutes(List<AttendanceInfo> normalAttendanceInfo)
        {
            var dayWorkingTimeTable = getWorkingTimeTable(normalAttendanceInfo);

            // 计算迟到早退时间
            _lateMiniutes = getLateMiniutes(normalAttendanceInfo, dayWorkingTimeTable);
            _earlyMiniutes = getEarlyMiniutes(normalAttendanceInfo, dayWorkingTimeTable);
        }

        /// <summary>
        /// 当日正常考勤获得的工时
        /// </summary>
        /// <param name="lateMiniutes"></param>
        /// <param name="earlyMiniutes"></param>
        /// <returns></returns>
        private void initNormalAttendanceManHours(int lateMiniutes, int earlyMiniutes)
        {
            // 每天标准工作时间 - 迟到早退时间
            TimeSpan manhours = _attendanceConfig.StdManhours -
                TimeSpan.FromMinutes(lateMiniutes) -
                TimeSpan.FromMinutes(earlyMiniutes);

            // 精确到 30 分钟 单位小时
            _normalAttendanceManhours = ((int)(manhours.TotalMinutes)).ToManhours();
        }

        /// <summary>
        /// 当日非正常考勤获得的工时
        /// </summary>
        /// <returns></returns>
        private void initAbnormalAttendanceManHours(
            IEnumerable<AttendanceInfo> abnormalAttendanceInfo,
            DayLeaveInfo dayLeaveInfo
            )
        {
            TimeSpan abnormalAttendanceManhours = TimeSpan.FromSeconds(0);

            // 工作假期时间 + 非正常考勤工时总和)

            // 非正常考勤工时总和计算

            // 对所有非正常考勤按时间排序
            var sortedAbnormalAttendanceList = abnormalAttendanceInfo.OrderBy(p => p.ActionTime);

            AttendanceInfo lastAttendanceInfo = null;
            // 寻找每一对非正常考勤)
            foreach (var attendanceInfo in sortedAbnormalAttendanceList)
            {
                if (attendanceInfo.AttendanceType == AttendanceType.offDuty)
                {
                    // 找到一次下班记录
                    if (lastAttendanceInfo != null &&
                        lastAttendanceInfo.AttendanceType == AttendanceType.onDuty
                        )
                    {
                        // 配对成功 累加
                        abnormalAttendanceManhours += (attendanceInfo.ActionTime - lastAttendanceInfo.ActionTime);
                        lastAttendanceInfo = null;
                    }
                    else
                    {
                        // 没有配对
                        lastAttendanceInfo = attendanceInfo;
                    }
                }
                else
                {
                    // 找到一次上班记录, 不作任何处理 继续找
                    lastAttendanceInfo = attendanceInfo;
                }
            }


            // 工作假期时间计算
            if (dayLeaveInfo.HasLeave)
            {
                // 全天都是假期的情况
                if (dayLeaveInfo.IsWholeDay && dayLeaveInfo.FirstLeave.IsForWork)
                {
                    // 算作当日全勤
                    abnormalAttendanceManhours = _attendanceConfig.StdManhours;
                }
                else
                {
                    // 累加每一次工作类型的假期
                    foreach (var leaveItem in dayLeaveInfo.Leaves)
                    {
                        if (leaveItem.IsForWork)
                        {
                            abnormalAttendanceManhours += (leaveItem.EndTime - leaveItem.StartTime);
                        }
                    }
                }

            }

            _abNormalAttendanceManhours = ((Int32)(abnormalAttendanceManhours.TotalMinutes)).ToManhours();
        }

        /// <summary>
        /// 取得一天的考勤评估
        /// </summary>
        /// <returns></returns>
        public DayAttendanceEvaluation GetAttendanceEvaluation()
        {
            // 初始化对象
            var resultEvaluationInfo = new DayAttendanceEvaluation
                                           {
                                               Id = Guid.Empty,
                                               UserId = _userId,
                                               Date = _dayInfo.Date,
                                               IsNeedToWork = _dayInfo.IsNeedToWork,
                                               LateMiniutes = _lateMiniutes,
                                               EarlyMiniutes = _earlyMiniutes,
                                               Manhours = getDayManHours(),
                                               IsAbsense = isAbsenseToday(),
                                               IsExtraWork = isExtraWorkToday()
                                           };
            return resultEvaluationInfo;
        }

        /// <summary>
        /// 判断今日是否加班
        /// </summary>
        /// <returns></returns>
        private bool isExtraWorkToday()
        {
            var manHours = getDayManHours();

            return !_dayInfo.IsNeedToWork && manHours > 0;
        }

        /// <summary>
        /// 判断今日是否缺勤
        /// </summary>
        /// <returns></returns>
        private bool isAbsenseToday()
        {
            var manHours = getDayManHours();

            return _dayInfo.IsNeedToWork && manHours == 0;
        }

        /// <summary>
        /// 获得一个标准上班考勤记录
        /// </summary>
        /// <param name="normalAttendanceInfo"></param>
        /// <returns></returns>
        private AttendanceInfo getNormalOffDutyInfo(IEnumerable<AttendanceInfo> normalAttendanceInfo)
        {
            return normalAttendanceInfo.Where(
                p => p.IsAbnormalAttendance == false &&
                    p.AttendanceType == AttendanceType.offDuty
                ).Single();
        }

        /// <summary>
        /// 获得一个标准下班考勤记录
        /// </summary>
        /// <param name="normalAttendanceInfo"></param>
        /// <returns></returns>
        private AttendanceInfo getNormalOnDutyInfo(IEnumerable<AttendanceInfo> normalAttendanceInfo)
        {
            return normalAttendanceInfo.Where(
                p => p.IsAbnormalAttendance == false &&
                    p.AttendanceType == AttendanceType.onDuty
                ).Single();
        }

        /// <summary>
        /// 获取当天的工作时间表，标准上班时间 标准下班时间
        /// </summary>
        /// <param name="normalAttendanceInfo"></param>
        /// <returns></returns>
        private DayWorkingTimeTable getWorkingTimeTable(IEnumerable<AttendanceInfo> normalAttendanceInfo)
        {
            var date = normalAttendanceInfo.First().Date;
            var stdOnDutyTime = _attendanceConfig.StdOnDutyTime.ToDateTime(date);

            var onDutyInfo = getNormalOnDutyInfo(normalAttendanceInfo);

            // 允许的 标准上班时间段
            var stdOnDutyTimeMin = stdOnDutyTime.Add(-_attendanceConfig.OnDutyOffsetForward);
            var stdOnDutyTimeMax = stdOnDutyTime.Add(_attendanceConfig.OnDutyOffsetAfterward);

            // 计算出实际计算的上班时间
            DateTime actualOnDutyTime;
            if (onDutyInfo.ActionTime < stdOnDutyTimeMin)
            {
                actualOnDutyTime = stdOnDutyTimeMin;
            }
            else if (onDutyInfo.ActionTime > stdOnDutyTimeMax)
            {
                actualOnDutyTime = stdOnDutyTimeMax;
            }
            else
            {
                actualOnDutyTime = onDutyInfo.ActionTime;
            }

            // 下班时间 = 实际上班时间 + 每日工时 + 午休时间
            DateTime actualOffDutyTime =
                actualOnDutyTime
                .Add(_attendanceConfig.StdManhours)
                .Add(_attendanceConfig.LunchBreakTime);

            var dayWorkingTimeTable = new DayWorkingTimeTable()
            {
                StdOnDutyTime = actualOnDutyTime,
                StdOffDutyTime = actualOffDutyTime,
            };

            return dayWorkingTimeTable;
        }

        /// <summary>
        /// 获得早退时间
        /// </summary>
        /// <param name="normalAttendanceInfo"></param>
        /// <param name="dayWorkingTimeTable"></param>
        /// <returns></returns>
        private int getEarlyMiniutes(IEnumerable<AttendanceInfo> normalAttendanceInfo, DayWorkingTimeTable dayWorkingTimeTable)
        {
            int earlyMiniutes = 0;
            var offDutyInfo = getNormalOffDutyInfo(normalAttendanceInfo);

            if (offDutyInfo.ActionTime < dayWorkingTimeTable.StdOffDutyTime)
            {
                earlyMiniutes = (int)
                    ((dayWorkingTimeTable.StdOffDutyTime - offDutyInfo.ActionTime)
                    .TotalMinutes);
            }
            return earlyMiniutes;
        }

        /// <summary>
        /// 获得迟到时间
        /// </summary>
        /// <param name="normalAttendanceInfo"></param>
        /// <param name="dayWorkingTimeTable"></param>
        /// <returns></returns>
        private int getLateMiniutes(IEnumerable<AttendanceInfo> normalAttendanceInfo, DayWorkingTimeTable dayWorkingTimeTable)
        {
            int lateMiniutes = 0;
            var onDutyInfo = getNormalOnDutyInfo(normalAttendanceInfo);

            // 考虑迟到延迟时间
            if (onDutyInfo.ActionTime > dayWorkingTimeTable.StdOnDutyTime.Add(_attendanceConfig.StdOnDutyTimeDelay))
            {
                lateMiniutes = (int)
                    ((onDutyInfo.ActionTime - dayWorkingTimeTable.StdOnDutyTime)
                    .TotalMinutes);
            }
            return lateMiniutes;
        }






        /// <summary>
        /// 获取当日包含的所有非正常考勤记录
        /// </summary>
        /// <returns></returns>
        private IEnumerable<AttendanceInfo> getAbnormalAttendanceInfo(List<AttendanceInfo> attendanceInfoList)
        {
            // 非正常考勤如果中间插入了一个正常考勤，也许出错，但是情况太过特殊，暂时不考虑..

            return attendanceInfoList
                .Where(p => p.IsAbnormalAttendance == true)
                .ToList();
        }

        /// <summary>
        /// 尝试获取当日包含的正常考勤记录
        /// </summary>
        /// <param name="attendanceInfoList"></param>
        /// <param name="normalAttendanceInfo"></param>
        /// <returns>如果当日存在正常考勤记录，则返回true，否则返回false</returns>
        private bool tryGetNormalAttendanceInfo(List<AttendanceInfo> attendanceInfoList, out List<AttendanceInfo> outNormalAttendanceInfoList)
        {
            var normalAttendanceInfoList = attendanceInfoList
                .Where(p => p.IsAbnormalAttendance == false)
                .ToList();

            // 每天有两次正常考勤，一次上班一次下班
            if (normalAttendanceInfoList.Count == 2 &&
                normalAttendanceInfoList[0].AttendanceType != normalAttendanceInfoList[1].AttendanceType)
            {
                outNormalAttendanceInfoList = normalAttendanceInfoList;
                return true;
            }

            outNormalAttendanceInfoList = null;
            return false;
        }




        /// <summary>
        /// 当日获得的工时
        /// </summary>
        /// <returns></returns>
        private int getDayManHours()
        {
            // 8小时封顶
            var dayManhours =
                _normalAttendanceManhours.GetValueOrDefault(0) +
                _abNormalAttendanceManhours.GetValueOrDefault(0) +
                _extraManhours;
            var maxManhours = ((int)(_attendanceConfig.StdManhours.TotalMinutes)).ToManhours();

            return Math.Min(dayManhours, maxManhours);
        }



    }


}
